Skip to content

feat: attestation extension for eligibility claims#264

Open
douglasborthwick-crypto wants to merge 2 commits intoUniversal-Commerce-Protocol:mainfrom
douglasborthwick-crypto:feat/attestation-extension
Open

feat: attestation extension for eligibility claims#264
douglasborthwick-crypto wants to merge 2 commits intoUniversal-Commerce-Protocol:mainfrom
douglasborthwick-crypto:feat/attestation-extension

Conversation

@douglasborthwick-crypto
Copy link

@douglasborthwick-crypto douglasborthwick-crypto commented Mar 13, 2026

Description

Adds a capability extension that complements eligibility (#250) with
cryptographic attestation proofs for non-instrument claims (token holdings,
on-chain credentials, membership status).

Discussed and invited in #203 and #250:

How it works

  • Platforms relay signed attestations from third-party verifiers alongside
    context.eligibility claims
  • Businesses verify offline: fetch JWKS from provider_jwks, select key by
    kid, verify sig over payload
  • payload is an opaque passthrough of the provider's signed object — no
    transformation, so signature verification works end-to-end
  • expires_at lives outside payload because it is not covered by the
    signature — the Business checks this independently

Follows the discount extension pattern (allOf on Cart and Checkout, sibling
map keyed by reverse-domain eligibility claims).

Wire format example

{
  "context": {
    "eligibility": ["com.example.token_holder"]
  },
  "attestations": {
    "com.example.token_holder": {
      "provider_jwks": "https://example.com/.well-known/jwks.json",
      "kid": "example-key-2026-01",
      "payload": {
        "id": "att-7c3e9f",
        "pass": true,
        "results": ["..."],
        "attestedAt": "2026-03-13T19:20:32.530Z"
      },
      "sig": "base64...",
      "expires_at": "2026-03-13T19:50:32.530Z"
    }
  }
}

Verification flow

  1. Business receives checkout with context.eligibility claim + matching
    attestations entry
  2. Fetches JWKS from provider_jwks, selects key matching kid
  3. Verifies sig over the payload (platforms must preserve the provider's
    original serialization)
  4. Checks expires_at hasn't passed
  5. Reads payload.pass to confirm the claim is met

No callback to the Platform or attestation provider required — fully offline.

Relationship to #280

This PR and #280 (wallet attestation mechanism type) are complementary but
independent:

Neither PR depends on the other. When both are present, the mechanism provides
the signed attestation and the extension provides the wire format for attaching
it.


Files Changed

File Change
source/schemas/shopping/attestation.json New extension schema — attestation map on Cart and Checkout via allOf

Type of change

  • New feature (non-breaking change which adds functionality)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes

@douglasborthwick-crypto douglasborthwick-crypto requested review from a team as code owners March 13, 2026 22:17
@google-cla
Copy link

google-cla bot commented Mar 13, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@douglasborthwick-crypto
Copy link
Author

@googlebot I signed it!

@douglasborthwick-crypto
Copy link
Author

Hi @amithanda — just checking in on this one. Is there anything blocking review, or would adding the "TC review" label help get it into the queue? Happy to address any feedback.

Adds a capability extension that complements eligibility (Universal-Commerce-Protocol#250) with
cryptographic attestation proofs. Platforms relay signed attestations
alongside eligibility claims; Businesses verify offline via JWKS.

Follows the discount extension pattern (allOf on Cart/Checkout).
Discussed in Universal-Commerce-Protocol#203 and Universal-Commerce-Protocol#250 — igrigorik invited this as a companion
extension ("a great candidate, additive and non-breaking").
@zologic
Copy link

zologic commented Mar 24, 2026

Reviewing as co-author of the related Ideas discussion [#298](#298) (dev.ucp.shopping.eligibility) and independent implementer — UCPReady (WooCommerce) has built this verification flow in production.

The wire format here is consistent with what we designed in #298. A few observations from the implementation side:

Strong alignment with #298:

One implementation note:
The spec says "platforms must preserve the provider's original serialization" for sig verification to work end-to-end. This is critical and easy to get wrong — any JSON re-serialization between the attestation provider and the business will break the signature. Worth adding an explicit note in the schema or docs that payload must be treated as an opaque object and passed through without transformation or key reordering. Implementers who deserialize and re-serialize JSON (which most do) will hit this silently.

On expires_at format:
The example uses ISO 8601 (2026-03-13T19:50:32.530Z). Worth confirming this is RFC 3339 with UTC offset required (consistent with the rest of the UCP spec), not just ISO 8601 broadly.

Overall this PR is well-structured and the approach is sound. The payload serialization note is the main thing worth clarifying before merge.

@douglasborthwick-crypto
Copy link
Author

Thanks for the review, and for linking this to #298 — the two proposals are designed to work together.

On your two points:

Payload serialization: The schema does include this — the payload description reads: "Platforms MUST preserve the provider's original serialization to ensure signature verification succeeds." But you're right that it's easy to miss buried in a field description. Worth surfacing more prominently — happy to add a dedicated implementation note in the PR description or a top-level description in the schema if the TC prefers.

expires_at format: The schema uses "format": "date-time", which in JSON Schema is RFC 3339 (section 5.6) — consistent with checkout.json which uses the same format. The PR description example doesn't call this out explicitly though, so the ambiguity is fair. Can add "RFC 3339" to the expires_at description to match the convention in checkout.json.

Appreciate the implementation-side validation — having an independent implementer confirm the wire format aligns with a production system is exactly the kind of feedback the TC looks for.

…es_at

Address review feedback from zologic (Universal-Commerce-Protocol#264 comment):
- Promote payload opaqueness warning to top-level schema description
- Add explicit "RFC 3339" to expires_at description, matching checkout.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants